﻿namespace GoodAdmin.Core;
public class RedisCacheManager : IRedisCacheManager
{
    private readonly string redisConnenctionString;
    public volatile FullRedis redisConnection;
    private readonly object redisConnectionLock = new object();
    public RedisCacheManager()
    {
        string redisConfiguration = App.GetOptions<CacheOptions>().RedisConnectionString;
        if (string.IsNullOrWhiteSpace(redisConfiguration))
        {
            throw new ArgumentException("redis config is empty", nameof(redisConfiguration));
        }
        //FullRedis.Register();
        this.redisConnenctionString = redisConfiguration;
        this.redisConnection = GetRedisConnection();
    }
    public RedisCacheManager(string redisConfiguration, int Db = 0)
    {
        IDisposable a = redisConnection.AcquireLock("", 1);
        if (string.IsNullOrWhiteSpace(redisConfiguration))
        {
            throw new ArgumentException("redis config is empty", nameof(redisConfiguration));
        }
        //FullRedis.Register();
        this.redisConnenctionString = redisConfiguration;
        this.redisConnection = GetRedisConnection();
    }
    /// <summary>
    /// 核心代码，获取连接实例
    /// 通过双if 夹lock的方式，实现单例模式
    /// </summary>
    /// <returns></returns>
    private FullRedis GetRedisConnection()
    {
        //如果已经连接实例，直接返回
        if (this.redisConnection != null)
        {
            return this.redisConnection;
        }
        //加锁，防止异步编程中，出现单例无效的问题
        lock (redisConnectionLock)
        {
            if (this.redisConnection != null)
            {
                //释放redis连接
                this.redisConnection.Dispose();
            }
            try
            {
                this.redisConnection = FullRedis.Create(redisConnenctionString);
                this.redisConnection.Timeout = 5000;
            }
            catch (Exception ex)
            {
                throw new Exception("Redis服务未启用，请开启该服务" + ex.Message);
            }
        }
        return this.redisConnection;
    }
    #region 普通
    public TEntity Get<TEntity>(string key)
    {
        return redisConnection.Get<TEntity>(key);
    }
    /// <inheritdoc/>
    public void Set<TEntity>(string key, TEntity value)
    {
        if (value != null)
        {
            redisConnection.Set(key, value);
        }
    }
    /// <inheritdoc/>
    public void Set<TEntity>(string key, TEntity value, TimeSpan cacheTime)
    {
        if (value != null)
        {
            redisConnection.Set(key, value, cacheTime);
        }
    }
    /// <inheritdoc/>
    public void Set(string key, object value, TimeSpan cacheTime)
    {
        redisConnection.Set(key, value, cacheTime);
    }
    /// <inheritdoc/>
    public bool ContainsKey(string key)
    {
        return redisConnection.ContainsKey(key);
    }
    /// <inheritdoc/>
    public void Remove(string key)
    {
        redisConnection.Remove(key);
    }
    public void Remove(params string[] keys)
    {
        redisConnection.Remove(keys);
    }
    /// <inheritdoc/>
    public void Clear()
    {
        redisConnection.Clear();
    }
    /// <inheritdoc/>
    public void SetExpire(string key, TimeSpan timeSpan)
    {
        redisConnection.SetExpire(key, timeSpan);
    }
    /// <inheritdoc/>
    public async Task RemoveByKey(string key, int count)
    {
        await Task.Run(() =>
        {
            var keys = redisConnection.Search(key, count).ToList();
            foreach (var k in keys)
                redisConnection.Remove(k);
        });
    }
    /// <inheritdoc/>
    public async Task RemoveAllByKey(string key, int count = 999)
    {
        await Task.Run(() =>
        {
            while (true)
            {
                var keyList = redisConnection.Search(key, count).ToList();
                if (keyList.Count > 0)
                {
                    foreach (var k in keyList)
                        redisConnection.Remove(k);
                }
                else
                {
                    break;
                }
            }
        });
    }
    /// <inheritdoc/>
    public IDisposable AcquireLock(string key, int msTimeout)
    {
        return redisConnection.AcquireLock(key, msTimeout);
    }
    #endregion
    #region HashMap
    public RedisHash<string,TValue> GetHashMap<TValue>(string key)
    {
        return redisConnection.GetDictionary<TValue>(key) as RedisHash<string, TValue>;
    }
    /// <inheritdoc/>
    public bool HashSetWithResult<T>(string key, Dictionary<string, T> dic, int expire = -1)
    {        
        return GetHashMap<T>(key).HMSet(dic);
    }
    /// <inheritdoc/>
    public void HashSet<T>(string key, string field, T value, int expire = -1)
    {
        GetHashMap<T>(key).Add(field, value);        
    }
    /// <inheritdoc/>
    public bool HashSet<T>(string key, Dictionary<string, T> dic, int expire = -1)
    {
        return GetHashMap<T>(key).HMSet(dic);
    }
    /// <inheritdoc/>
    public string[] HashGet(string key, params string[] fields)
    {
        return GetHashMap<string>(key).HMGet(fields);
    }
    /// <inheritdoc/>
    public List<T> HashGet<T>(string key, params string[] fields)
    {
        return GetHashMap<T>(key).HMGet(fields).ToList();
    }

    public int HashDel<T>(string key, params string[] fields)
    {
        return GetHashMap<T>(key).HDel(fields);
    }

    public long HashIncrement<T>(string key, string field, int increment)
    {
        return GetHashMap<T>(key).HIncrBy(field, increment);
    }

    #endregion
    #region 队列
    /// <inheritdoc/>
    public int AddQueue<T>(string key, T[] value)
    {
        var queue = redisConnection.GetQueue<T>(key);
        return queue.Add(value);
    }
    /// <inheritdoc/>
    public List<T> GetQueue<T>(string key, int Count = 1)
    {
        var queue = redisConnection.GetQueue<T>(key);
        var result = queue.Take(Count).ToList();
        return result;
    }
    /// <inheritdoc/>
    public async Task<T> GetQueueOneAsync<T>(string key)
    {
        var queue = redisConnection.GetQueue<T>(key);
        return await queue.TakeOneAsync(1);
    }
    #endregion
    #region 可靠队列
    /// <inheritdoc/>
    public RedisReliableQueue<T> GetReliableQueue<T>(string key)
    {
        var queue = redisConnection.GetReliableQueue<T>(key);
        return queue;
    }
    /* 
    public async Task<List<T>> GetReliableQueueMsgAsync<T>(RedisReliableQueue<T> queue, int count)
    {
    return await Task.Run(() =>
    {
    return queue.Take(count).ToList();
    });
    }
    public async Task<T> GetReliableQueueMsgAsync<T>(RedisReliableQueue<T> queue)
    {
    return await Task.Run(() =>
    {
    return queue.TakeOne();
    });
    }
    public async Task<int> Acknowledge<T>(RedisReliableQueue<T> queue, T value)
    {
    return await Task.Run(() =>
    {
    var data = value.ToJson();
    var count = queue.Acknowledge(data);
    return count;
    });
    }
    */
    /// <inheritdoc/>
    public int RollbackAllAck(string key, int retryInterval = 60)
    {
        var queue = redisConnection.GetReliableQueue<string>(key);
        queue.RetryInterval = retryInterval;
        return queue.RollbackAllAck();
    }
    /// <inheritdoc/>
    public int AddReliableQueueList<T>(string key, List<T> value)
    {
        var queue = redisConnection.GetReliableQueue<T>(key);
        return queue.Add(value.ToArray());
    }
    /// <inheritdoc/>
    public int AddReliableQueue<T>(string key, T value)
    {
        var queue = redisConnection.GetReliableQueue<T>(key);
        return queue.Add(value);
    }
    #endregion
    #region 列表
    /// <inheritdoc/>
    public int AddList(string key, IEnumerable<string> values, TimeSpan timeSpan)
    {
        var list = redisConnection.GetList<string>(key) as RedisList<string>;
        redisConnection.SetExpire(key, timeSpan);
        return list.AddRange(values);
    }
    /// <inheritdoc/>
    public int AddList(string key, IEnumerable<string> values)
    {
        var list = redisConnection.GetList<string>(key) as RedisList<string>;
        return list.AddRange(values);
    }
    /// <inheritdoc/>
    public async Task<string> GetListAsync(string key, string value)
    {
        return await Task.Run(() =>
        {
            var list = redisConnection.GetList<string>(key) as RedisList<string>;
            return list[list.IndexOf(value)];
        });
    }
    #endregion
    #region SortSet
    /// <inheritdoc/>
    public RedisSortedSet<T> GetRedisSortedSet<T>(string key)
    {
        var zset = new RedisSortedSet<T>(redisConnection, key);
        return zset;
    }
    /// <inheritdoc/>
    public double SortedSetAdd(string key, Dictionary<string, double> value, string options = null)
    {
        var zset = new RedisSortedSet<string>(redisConnection, key);
        return zset.Add(options, value);
    }
    /// <inheritdoc/>
    public IDictionary<T, double> SortedSetPopMax<T>(string key, int count = 1)
    {
        var zset = new RedisSortedSet<T>(redisConnection, key);
        return zset.PopMax(count);
    }
    /// <inheritdoc/>
    public IDictionary<T, double> SortedSetPopMin<T>(string key, int count = 1)
    {
        var zset = new RedisSortedSet<T>(redisConnection, key);
        return zset.PopMin(count);
    }
    /// <inheritdoc/>
    public int SortedSetFindCount<T>(string key, double min, double max)
    {
        var zset = new RedisSortedSet<T>(redisConnection, key);
        return zset.FindCount(min, max);
    }
    /// <inheritdoc/>
    public T[] SortedSetRange<T>(string key, int start, int stop)
    {
        var zset = new RedisSortedSet<T>(redisConnection, key);
        return zset.Range(start, stop);
    }
    /// <inheritdoc/>
    public IDictionary<T, double> SortedSetRangeWithScores<T>(string key, int start, int stop)
    {
        var zset = new RedisSortedSet<T>(redisConnection, key);
        return zset.RangeWithScores(start, stop);
    }
    /// <inheritdoc/>
    public T[] SortedSetRangeByScore<T>(string key, double min, double max, int offset, int count)
    {
        var zset = new RedisSortedSet<T>(redisConnection, key);
        return zset.RangeByScore(min, max, offset, count);
    }
    /// <inheritdoc/>
    public async Task<T[]> SortedSetRangeByScoreAsync<T>(string key, double min, double max, int offset, int count)
    {
        var zset = new RedisSortedSet<T>(redisConnection, key);
        return await zset.RangeByScoreAsync(min, max, offset, count);
    }
    /// <inheritdoc/>
    public IDictionary<T, double> SortedSetRangeByScoreWithScores<T>(string key, double min, double max, int offset, int count)
    {
        var zset = new RedisSortedSet<T>(redisConnection, key);
        return zset.RangeByScoreWithScores(min, max, offset, count);
    }
    /// <inheritdoc/>
    public int SortedSetRank<T>(string key, T member)
    {
        var zset = new RedisSortedSet<T>(redisConnection, key);
        return zset.Rank(member);
    }
    /// <inheritdoc/>
    public IEnumerable<KeyValuePair<T, double>> SortedSetSearch<T>(string key, string pattern, int count, int position = 0)
    {
        var zset = new RedisSortedSet<T>(redisConnection, key);
        return zset.Search(pattern, count, position);
    }
    /// <inheritdoc/>
    public double SortedSetIncrement<T>(string key, T member, double score)
    {
        var zset = new RedisSortedSet<T>(redisConnection, key);
        return zset.Increment(member, score);
    }
    #endregion
    #region Set
    /// <inheritdoc/>
    public void SetAdd<T>(string key, T value)
    {
        var set = redisConnection.GetSet<T>(key);
        set.Add(value);
    }
    /// <inheritdoc/>
    public void SetAddList<T>(string key, T[] value)
    {
        var set = redisConnection.GetSet<string>(key);
        var set2 = set as RedisSet<T>;
        set2.SAdd(value);
    }
    /// <inheritdoc/>
    public bool SetRemove<T>(string key, T value)
    {
        var set = redisConnection.GetSet<T>(key);
        return set.Remove(value);
    }
    /// <inheritdoc/>
    public bool SetContains<T>(string key, T value)
    {
        var set = redisConnection.GetSet<T>(key);
        return set.Contains(value);
    }
    /// <inheritdoc/>
    public void SetClear<T>(string key)
    {
        var set = redisConnection.GetSet<T>(key);
        set.Clear();
    }
    /// <inheritdoc/>
    public void SetCopyTo<T>(string key, T[] array, int arrayIndex)
    {
        var set = redisConnection.GetSet<T>(key);
        set.CopyTo(array, arrayIndex);
    }
    #endregion
}